package com.mxx.common.data.jpa.auditing;

import com.mxx.common.data.jpa.UpdateSpecification;
import com.mxx.common.exception.CommentException;
import org.springframework.data.jpa.domain.Specification;

public interface SpecInterceptor {

    void select(SpecState state);

    void update(SpecState state);

    void delete(SpecState state);

    int sort();

    enum SpecType {
        SELECT,
        UPDATE,
        DELETE,
        ;
    }


    class SpecState {
        private SpecType originalType;
        private SpecType currentType;
        private Specification spec;
        private Class domainClass;
        private boolean modify = false;

        public SpecState(SpecType originalType, Class domainClass, Specification spec) {
            this.originalType = originalType;
            this.currentType = originalType;
            this.domainClass = domainClass;
            this.spec = spec;
        }


        public static SpecState select(Class domainClass, Specification spec) {
            return new SpecState(SpecType.SELECT, domainClass, spec);
        }

        public static SpecState delete(Class domainClass, Specification spec) {
            return new SpecState(SpecType.DELETE, domainClass, spec);
        }

        public static SpecState update(Class domainClass, UpdateSpecification spec) {
            return new SpecState(SpecType.UPDATE, domainClass, spec);
        }


        public Specification getSpec() {
            return spec;
        }

        public UpdateSpecification getUpdateSpec() {
            return (UpdateSpecification) spec;
        }

        public void toUpdate(UpdateSpecification spec) {
            if (spec == null) {
                throw new CommentException("spec is null");
            }
            if (originalType == SpecType.SELECT) {
                throw new CommentException("select statement does not support state transition");
            } else if (!this.modify && currentType == SpecType.DELETE) {
                this.currentType = SpecType.UPDATE;
                this.spec = spec;
                this.modify = true;
            }
        }

        public void flush(Specification spec) {
            if (spec == null) {
                throw new CommentException("spec is null");
            }
            this.spec = spec;
        }

        public void toDelete(UpdateSpecification spec) {
            if (spec == null) {
                throw new CommentException("spec is null");
            }
            if (originalType == SpecType.SELECT) {
                throw new CommentException("select statement does not support state transition");
            } else if (!this.modify && currentType == SpecType.UPDATE) {
                this.currentType = SpecType.DELETE;
                this.spec = spec;
                this.modify = true;
            }
        }

        public SpecType getCurrentType() {
            return currentType;
        }

        public SpecType getOriginalType() {
            return originalType;
        }

        public boolean isModify() {
            return this.modify;
        }

        public Class getDomainClass() {
            return domainClass;
        }
    }
}
